package com.zl.elasticsearchdemo;

import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.DocWriteResponse;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.*;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.*;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;

import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.junit.Assert.*;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: ZhangLiang
 * @Date: 2021/08/05/17:09
 * @Description:
 */
@Component
public class ESRequestUtils {


    private final RestHighLevelClient client;

    public ESRequestUtils(RestHighLevelClient client) {
        this.client = client;
    }


    public void addIndex(String index, String type, String documentId, String jsonString) {
        IndexRequest request = new IndexRequest(index, type, documentId);

//       1. json格式
//        String jsonString = "{" +
//                "\"user\":\"kimchy\"," +
//                "\"postDate\":\"2013-01-30\"," +
//                "\"message\":\"trying out Elasticsearch\"" +
//                "}";
        request.source(jsonString, XContentType.JSON);

//        2.作为自动转换为 JSON 格式的文档源提供Map
//        Map<String, Object> jsonMap = new HashMap<>();
//        jsonMap.put("user", "kimchy");
//        jsonMap.put("postDate", new Date());
//        jsonMap.put("message", "trying out Elasticsearch");
//        IndexRequest indexRequest = new IndexRequest("posts", "doc", "1")
//                .source(jsonMap);

//        3.作为对象提供的文档源，弹性搜索内置帮助器生成 JSON 内容XContentBuilder
//        XContentBuilder builder = XContentFactory.jsonBuilder();
//        builder.startObject();
//        {
//            builder.field("user", "kimchy");
//            builder.timeField("postDate", new Date());
//            builder.field("message", "trying out Elasticsearch");
//        }
//        builder.endObject();
//        IndexRequest indexRequest = new IndexRequest("posts", "doc", "1")
//                .source(builder);

//        4.作为键对提供的文档源，可转换为 JSON 格式Object
//        IndexRequest indexRequest = new IndexRequest("posts", "doc", "1")
//                .source("user", "kimchy",
//                        "postDate", new Date(),
//                        "message", "trying out Elasticsearch");


//        可选参数编辑
//        可选择提供以下参数：
//        request.routing("routing");
//        路由值

//        request.parent("parent");
//        父值

//        request.timeout(TimeValue.timeValueSeconds(1));
//        request.timeout("1s");
//        超时等待主要碎片成为可用的TimeValue
//                超时等待主要碎片成为可用的String

//        request.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
//        request.setRefreshPolicy("wait_for");
//        刷新策略作为实例WriteRequest.RefreshPolicy
//                刷新策略作为String

//        request.version(2);
//        版本

//        request.versionType(VersionType.EXTERNAL);
//        版本类型

//        request.opType(DocWriteRequest.OpType.CREATE);
//        request.opType("create");
//        作为值提供的操作类型DocWriteRequest.OpType
//        作为：可以或（默认）提供的操作类型Stringcreateupdate

//        request.setPipeline("pipeline");
//        在将文档索引之前执行的摄入管道的名称

        try {
//            同步执行
            IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);

//            异步执行
//            client.indexAsync(request, RequestOptions.DEFAULT, listener);

//            String index = indexResponse.getIndex();
//            String type = indexResponse.getType();
//            String id = indexResponse.getId();
            long version = indexResponse.getVersion();
            if (indexResponse.getResult() == DocWriteResponse.Result.CREATED) {
                System.out.println("处理（如果需要）首次创建文档的情况");
            } else if (indexResponse.getResult() == DocWriteResponse.Result.UPDATED) {
                System.out.println("处理（如果需要）文件被重写的情况，因为它已经存在\n" +
                        "\n");
            }
            ReplicationResponse.ShardInfo shardInfo = indexResponse.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                System.out.println("处理成功碎片数量小于总碎片的情况\n" +
                        "\n");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure :
                        shardInfo.getFailures()) {
                    String reason = failure.reason();
                    System.out.println("处理潜在的故障\n" +
                            "\n");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.CONFLICT) {
                System.out.println("版本冲突或者如果设置为具有相同索引、类型和 ID 的文档，也会发生相同的情况");
            }

        }
    }

    /**
     * 获取
     * 类型默认为doc
     *
     * @param index      索引
     * @param documentId 节点id
     * @return 返回获取的json数据
     */
    public String getRequest(String index, String type, String documentId) {
        GetRequest getRequest = new GetRequest(index, type, documentId);

//         禁用源检索，默认启用
//        getRequest.fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE);


//        为特定字段配置源内含
//        String[] includes = new String[]{"message", "*Date"};
//        String[] excludes = Strings.EMPTY_ARRAY;
//        FetchSourceContext fetchSourceContext =
//                new FetchSourceContext(true, includes, excludes);
//        getRequest.fetchSourceContext(fetchSourceContext);


//        为特定字段配置源排除
//        String[] includes = Strings.EMPTY_ARRAY;
//        String[] excludes = new String[]{"message"};
//        FetchSourceContext fetchSourceContext =
//                new FetchSourceContext(true, includes, excludes);
//        getRequest.fetchSourceContext(fetchSourceContext);


//        为特定存储的字段配置检索（要求字段单独存储在映射中）
//         getRequest.storedFields("message");


//        检索存储的字段（要求字段单独存储在映射中）message
        GetResponse getResponse = null;
        try {
//        同步执行
            getResponse = client.get(getRequest, RequestOptions.DEFAULT);

//            String index = getResponse.getIndex();
//            String type = getResponse.getType();
//            String id = getResponse.getId();
            if (getResponse.isExists()) {
                long version = getResponse.getVersion();
                String sourceAsString = getResponse.getSourceAsString();
                Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
                byte[] sourceAsBytes = getResponse.getSourceAsBytes();


                System.out.println("检索文档作为String: " + sourceAsString);
                System.out.println("检索文档作为Map<String, Object>" + sourceAsMap);
                System.out.println("检索文档作为byte[]" + sourceAsBytes);
                return sourceAsString;
            } else {
                System.out.println("未找到文档");
            }
        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {
                System.out.println("处理因索引不存在而抛出的异常");
            } else if (e.status() == RestStatus.CONFLICT) {
                System.out.println("异常表示返回了版本冲突错误");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
//        String message = getResponse.getField("message").getValue();

//        异步执行
//     client.getAsync(getRequest, RequestOptions.DEFAULT, new ActionListenerImpl());
        return "";
    }

    class ActionListenerImpl implements ActionListener<GetResponse> {

        @Override
        public void onResponse(GetResponse documentFields) {

        }

        @Override
        public void onFailure(Exception e) {

        }
    }


    /**
     * 验证是否存在索引
     *
     * @param index
     * @param type
     * @param documentId
     * @return
     */
    public Boolean exists(String index, String type, String documentId) {
        GetRequest getRequest = new GetRequest(index, type, documentId);

//        禁用。_source
        getRequest.fetchSourceContext(new FetchSourceContext(false));
//        禁用存储字段。
        getRequest.storedFields("_none_");
        boolean exists;
        try {
            //同步
            exists = client.exists(getRequest, RequestOptions.DEFAULT);
            return exists;
//            异步执行
//            client.existsAsync(getRequest, RequestOptions.DEFAULT, listener);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 删除节点
     *
     * @param index
     * @param type
     * @param documentId
     */
    public void delete(String index, String type, String documentId) {
        DeleteRequest deleteRequest = new DeleteRequest(index, type, documentId);
//        路由值
//        deleteRequest.routing("routing");
//        父值
//        deleteRequest.parent("parent");

//        超时等待
//        deleteRequest.timeout(TimeValue.timeValueMinutes(2));
//        deleteRequest.timeout("2m");

//        刷新策略作为实例WriteRequest.RefreshPolicy
//        deleteRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
//        刷新策略作为String
//        deleteRequest.setRefreshPolicy("wait_for");

//        版本类型
//        deleteRequest.versionType(VersionType.EXTERNAL);
        try {
//            同步执行
            DeleteResponse deleteResponse = client.delete(
                    deleteRequest, RequestOptions.DEFAULT);

            if (deleteResponse.getResult() == DocWriteResponse.Result.NOT_FOUND) {
                System.out.println("未发现了该文档");
            }
//            删除响应
//            String index = deleteResponse.getIndex();
//            String type = deleteResponse.getType();
//            String id = deleteResponse.getId();
            long version = deleteResponse.getVersion();
            ReplicationResponse.ShardInfo shardInfo = deleteResponse.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                System.out.println("处理成功碎片数量小于总碎片的情况");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure :
                        shardInfo.getFailures()) {
                    String reason = failure.reason();
                    System.out.println("处理潜在故障: " + reason);
                }
            }

//              异步执行
//            client.deleteAsync(request, RequestOptions.DEFAULT, listener);
        } catch (ElasticsearchException exception) {
            if (exception.status() == RestStatus.CONFLICT) {
                System.out.println("版本冲突");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public void update(String index, String type, String documentId) {
        UpdateRequest request = new UpdateRequest(index, type, documentId);

//       1. 内联脚本更新
//        作为对象提供的脚本参数Map
//        Map<String, Object> parameters = singletonMap("count", 4);
//        使用语言和以前的参数创建内联脚本painless
//        Script inline = new Script(ScriptType.INLINE, "painless",
//                "ctx._source.field += params.count", parameters);
//        将脚本设置为更新请求
//        request.script(inline);

//        2.存储的脚本更新
//        引用以语言名称存储的脚本increment-fieldpainless
//        Script stored = new Script(
//                ScriptType.STORED, null, "increment-field", parameters);
//        在更新请求中设置脚本
//        request.script(stored);

//        3.带有部分文档的更新时，部分文档将与现有文档合并
//       3.1 部分文档源以 JSON 格式提供String


        String jsonString = "{" +
                "\"last_name\":\"tom\"," +
                "\"age\":\"25\"" +
                "}";
        request.doc(jsonString, XContentType.JSON);

//        3.2 作为自动转换为 JSON 格式的部分文档源提供Map
//        Map<String, Object> jsonMap = new HashMap<>();
//        jsonMap.put("updated", new Date());
//        jsonMap.put("reason", "daily update");
//        UpdateRequest request = new UpdateRequest("posts", "doc", "1")
//                .doc(jsonMap);
//        3.3 为对象提供的部分文档源，弹性搜索内置帮助器生成 JSON 内容XContentBuilder
//        XContentBuilder builder = XContentFactory.jsonBuilder();
//        builder.startObject();
//        {
//            builder.timeField("updated", new Date());
//            builder.field("reason", "daily update");
//        }
//        builder.endObject();
//        UpdateRequest request = new UpdateRequest("posts", "doc", "1")
//                .doc(builder);

//        3.4 作为键对提供的部分文档源，可转换为 JSON 格式Object
//        UpdateRequest request = new UpdateRequest("posts", "doc", "1")
//                .doc("updated", new Date(),
//                        "reason", "daily update");


//        如果文档尚未存在，则可以使用该方法定义将插入为新文档的某些内容：upsert

//        String jsonString = "{\"created\":\"2017-01-01\"}";
//        request.upsert(jsonString, XContentType.JSON);

        try {
            //        启用源检索，默认禁用
            request.fetchSource(true);

//            同步执行
            UpdateResponse updateResponse = client.update(
                    request, RequestOptions.DEFAULT);


//            异步执行
//            client.updateAsync(request, RequestOptions.DEFAULT, listener);

//            当通过取源方法启用源检索时，响应包含更新文档的源,启用源检索在上面
            GetResult result = updateResponse.getGetResult();
            if (result.isExists()) {
                String sourceAsString = result.sourceAsString();
                Map<String, Object> sourceAsMap = result.sourceAsMap();
                byte[] sourceAsBytes = result.source();
                System.out.println("更新返回结果");
                System.out.println(sourceAsString);
            } else {
                System.out.println("未存在更新");
            }

            ReplicationResponse.ShardInfo shardInfo = updateResponse.getShardInfo();
            if (shardInfo.getTotal() != shardInfo.getSuccessful()) {
                System.out.println("处理成功碎片数量小于总碎片的情况");
            }
            if (shardInfo.getFailed() > 0) {
                for (ReplicationResponse.ShardInfo.Failure failure :
                        shardInfo.getFailures()) {
                    String reason = failure.reason();
                    System.out.println("处理潜在的故障");
                }
            }

//            String index = updateResponse.getIndex();
//            String type = updateResponse.getType();
//            String id = updateResponse.getId();
            long version = updateResponse.getVersion();
            if (updateResponse.getResult() == DocWriteResponse.Result.CREATED) {
                System.out.println("处理首次创建文档的情况");
            } else if (updateResponse.getResult() == DocWriteResponse.Result.UPDATED) {
                System.out.println("处理文件更新的情况");
            } else if (updateResponse.getResult() == DocWriteResponse.Result.DELETED) {
                System.out.println("处理文件被删除的情况");
            } else if (updateResponse.getResult() == DocWriteResponse.Result.NOOP) {
                System.out.println("处理文档未受更新影响的情况，即文档上未执行任何操作");
            }

        } catch (ElasticsearchException e) {
            if (e.status() == RestStatus.NOT_FOUND) {

            } else if (e.status() == RestStatus.CONFLICT) {
                System.out.println("版本冲突");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /**
     * 可以批量处理增删查改
     *
     * @param index
     * @param type
     * @param documentId
     */
    public void crudBulkRequest(String index, String type, String documentId) {
//        可用于使用单个请求执行多个索引、更新和/或删除操作。BulkRequest
        BulkRequest request = new BulkRequest();
        request.add(new DeleteRequest("posts", "doc", "3"));
        request.add(new UpdateRequest("posts", "doc", "2")
                .doc(XContentType.JSON, "other", "test"));
        request.add(new IndexRequest("posts", "doc", "4")
                .source(XContentType.JSON, "field", "baz"));


        try {
//            同步
            BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
//            异步
            //            client.bulkAsync(request, RequestOptions.DEFAULT, listener);

            if (bulkResponse.hasFailures()) {
                System.out.println("一个或多个操作是否失败");
                for (BulkItemResponse bulkItemResponse : bulkResponse) {
                    if (bulkItemResponse.isFailed()) {  //指定操作是否失败
                        BulkItemResponse.Failure failure =
                                bulkItemResponse.getFailure();
//                        检索失败操作的故障
                    }
                }
            }

            for (BulkItemResponse bulkItemResponse : bulkResponse) {
                DocWriteResponse itemResponse = bulkItemResponse.getResponse();

                switch (bulkItemResponse.getOpType()) {
                    case INDEX:
                        System.out.println("索引操作");
                    case CREATE:
                        IndexResponse indexResponse = (IndexResponse) itemResponse;
                        System.out.println("新增");
                        break;
                    case UPDATE:
                        UpdateResponse updateResponse = (UpdateResponse) itemResponse;
                        System.out.println("修改");
                        break;
                    case DELETE:
                        DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
                        System.out.println("删除");
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /**
     * 多get
     *
     * @param index
     * @param type
     * @param documentId
     */
    public void multiGetRequest(String index, String type, String documentId) {
        MultiGetRequest request = new MultiGetRequest();
        request.add(new MultiGetRequest.Item(index, type, documentId));
        request.add(new MultiGetRequest.Item("", "", ""));

//        禁用源检索，默认启用
//        request.add(new MultiGetRequest.Item("index", "type", "example_id")
//                .fetchSourceContext(FetchSourceContext.DO_NOT_FETCH_SOURCE));

        try {
//            同步执行
            MultiGetResponse response = client.mget(request, RequestOptions.DEFAULT);

//            当对不存在的索引执行的子查询之一将包含一个例外：getFailure
//            assertNull(missingIndexItem.getResponse());
//            Exception e = missingIndexItem.getFailure().getFailure();
//            ElasticsearchException ee = (ElasticsearchException) e;
// TODO status is broken! fix in a followup
// assertEquals(RestStatus.NOT_FOUND, ee.status());
//            assertThat(e.getMessage(),
//                    containsString("reason=no such index"));
//            getResponse为空。
//            getFailure不是并且包含一个。Exception
//                    这实际上是一个ExceptionElasticsearchException
//            并且它具有.如果不是多重获取，这将是一个HTTP 404。NOT_FOUND
//            getMessage解释实际原因。no such index

            MultiGetItemResponse firstItem = response.getResponses()[0];
//            getFailure返回无效，因为没有失败。
            assertNull(firstItem.getFailure());
//            getResponse返回 。GetResponse
            GetResponse firstGet = firstItem.getResponse();
//            String index = firstItem.getIndex();
//            String type = firstItem.getType();
//            String id = firstItem.getId();
            if (firstGet.isExists()) {
                long version = firstGet.getVersion();
                String sourceAsString = firstGet.getSourceAsString();
                Map<String, Object> sourceAsMap = firstGet.getSourceAsMap();
                byte[] sourceAsBytes = firstGet.getSourceAsBytes();
            } else {
                System.out.println("处理未找到文档的情况");
            }
            //                 异步执行
            //            client.mgetAsync(request, RequestOptions.DEFAULT, listener);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     * 根据索引查询返回的值
     *
     * @param index
     * @return
     */
    public List<String> searchRequest(String index) {
        //默认查询
//        SearchRequest request = new SearchRequest();

//        将请求限制为索引
        SearchRequest request = new SearchRequest();
        //        包含索引
        request.indices(index);
//        路由routing=“routing”
//        request.routing("routing");
//        设置控制如何解决不可用的指数以及通配符表达式的扩展方式IndicesOptions
//        request.indicesOptions(IndicesOptions.lenientExpandOpen());
//        使用首选项参数（例如执行搜索）来偏爱本地碎片。默认值是跨碎片随机化。
//        request.preference("_local");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//

//        2.创建一个完整的文本匹配查询，匹配当前索引字段"first_name"的值等于"Douglas"的值。
        MatchQueryBuilder matchQueryBuilder = new MatchQueryBuilder("first_name", "Douglas");
        matchQueryBuilder.fuzziness(Fuzziness.AUTO);  //  启用匹配查询上的模糊匹配
        matchQueryBuilder.prefixLength(3);  //  在匹配查询上设置前缀长度选项
        matchQueryBuilder.maxExpansions(10); //设置最大扩展选项以控制查询的模糊过程

//       1. 设置查询。可以是任何类型的QueryBuilde
//        searchSourceBuilder.query(QueryBuilders.matchAllQuery());  //所有查询
        searchSourceBuilder.query(matchQueryBuilder);
        //        设置决定结果索引的选项，以便开始搜索。默认值为0
        searchSourceBuilder.from(0);
//        设置可决定返回的搜索点击次数的选项。默认值为10。size
        searchSourceBuilder.size(5);
//        设置可选超时，控制允许搜索需要多长时间
//        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));


//        排序下降（默认值）_score
//        searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));
//        也按字段排序上升_id
        searchSourceBuilder.sort(new FieldSortBuilder("_id").order(SortOrder.ASC));

        request.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = client.search(request, RequestOptions.DEFAULT);
            RestStatus status = searchResponse.status();
//            执行时间
            TimeValue took = searchResponse.getTook();
//            请求是提前终止
            Boolean terminatedEarly = searchResponse.isTerminatedEarly();
            boolean timedOut = searchResponse.isTimedOut();
            int totalShards = searchResponse.getTotalShards();
            int successfulShards = searchResponse.getSuccessfulShards();
            int failedShards = searchResponse.getFailedShards();
            for (ShardSearchFailure failure : searchResponse.getShardFailures()) {
                // failures should be handled here
                System.out.println("失败");
            }
            SearchHits hits = searchResponse.getHits();

            long totalHits = hits.getTotalHits();
            float maxScore = hits.getMaxScore();
            List<String> jsonString = new ArrayList<>();
            for (SearchHit hit : hits) {
                // do something with the SearchHit
//                String index = hit.getIndex();
//                String type = hit.getType();
//                String id = hit.getId();
                float score = hit.getScore();

                //返回
                String sourceAsString = hit.getSourceAsString();
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                System.out.println("返回结果");
                System.out.println(sourceAsString);
                jsonString.add(sourceAsString);
//                String documentTitle = (String) sourceAsMap.get("title");
//                List<Object> users = (List<Object>) sourceAsMap.get("user");
//                Map<String, Object> innerObject =
//                        (Map<String, Object>) sourceAsMap.get("innerObject");
            }
            return jsonString;
//            client.searchAsync(searchRequest, RequestOptions.DEFAULT, listener);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 滚动 API 可用于从搜索请求中检索大量结果。
     * 单一根据索引查询
     *
     * @param index
     * @return
     */

    public List<String> searchScroll(String index) {
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(matchQuery("about", "I"));
        searchSourceBuilder.size(10);
        searchRequest.source(searchSourceBuilder);

//        设置滚动间隔
        searchRequest.scroll(TimeValue.timeValueMinutes(1L));
        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

//            阅读返回的滚动 ID，该 ID 指向正在保持活力的搜索上下文，并将在以下搜索滚动呼叫中需要
            String scrollId = searchResponse.getScrollId();
//            检索第一批搜索点击率
            SearchHit[] hits = searchResponse.getHits().getHits();
            System.out.println("结果");
            System.out.println(hits.length);
            List<String> jsonStrings = new ArrayList<>();
            for (SearchHit h : hits
            ) {
                String jsonString = h.getSourceAsString();
                System.out.println(jsonString);
                jsonStrings.add(jsonString);
            }

            while (hits != null && hits.length > 0) {

//            通过设置所需的滚动 ID 和滚动间隔创建SearchScrollRequest
                SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);
                scrollRequest.scroll(TimeValue.timeValueSeconds(30));
//            scrollRequest.scroll("60s");
//            同步
                SearchResponse searchScrollResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
//            异步
//            client.scrollAsync(scrollRequest, RequestOptions.DEFAULT, scrollListener);
                scrollId = searchScrollResponse.getScrollId();
                hits = searchScrollResponse.getHits().getHits();
                for (SearchHit h : hits
                ) {
                    String jsonString = h.getSourceAsString();
//                    System.out.println(jsonString);
                    jsonStrings.add(jsonString);
                }
            }
            //根据最后scrollId清除上下文
            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.addScrollId(scrollId);
//            同步
            ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
//            异步
//            client.clearScrollAsync(request, RequestOptions.DEFAULT, listener);
//            如果请求成功，请返回true
            boolean succeeded = clearScrollResponse.isSucceeded();

//            返回已发布的搜索上下文的数目
            int released = clearScrollResponse.getNumFreed();

            System.out.println("最终结果");
            System.out.println(hits.length);
            System.out.println(jsonStrings.size());
            return jsonStrings;
        } catch (IOException e) {
            e.printStackTrace();
        }


        return null;
    }


    public List<String> multiSearch(List<MultiSearchEntity> indexs) {
        MultiSearchRequest request = new MultiSearchRequest();

        for (MultiSearchEntity e : indexs
        ) {
            SearchRequest searchRequest = new SearchRequest();


            searchRequest.indices(e.getIndex());
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//             根据实际修改此匹配
            searchSourceBuilder.query(QueryBuilders.multiMatchQuery(e.getText(),e.getFieldNames()));
           searchSourceBuilder.size(20);  // 不设置大小查询默认10条
            searchRequest.source(searchSourceBuilder);
            //        设置滚动间隔
            searchRequest.scroll(TimeValue.timeValueMinutes(1L));

            request.add(searchRequest);
        }


        try {
            MultiSearchResponse response = client.msearch(request, RequestOptions.DEFAULT);
            MultiSearchResponse.Item[] items = response.getResponses();
            for (MultiSearchResponse.Item i : items
            ) {
//                assertNull(i.getFailure());
                SearchResponse searchResponse = i.getResponse();
                //            阅读返回的滚动 ID，该 ID 指向正在保持活力的搜索上下文，并将在以下搜索滚动呼叫中需要
//                assertEquals(4, searchResponse.getHits().getTotalHits());
                SearchHit[] hits = searchResponse.getHits().getHits();
                for (SearchHit h : hits
                ) {
                    String jsonString = h.getSourceAsString();
                    System.out.println(jsonString);
                }
                System.out.println("分割线---------------……~*---------------------");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
//        client.searchAsync(searchRequest, RequestOptions.DEFAULT, listener);


        return null;
    }
}
